package com.tompai.ftpan.web.service.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.tompai.ftpan.web.config.FeitianProperties;
import com.tompai.ftpan.web.dao.ResourceChunkMapper;
import com.tompai.ftpan.web.dao.ResourceMapper;
import com.tompai.ftpan.web.dto.MergeFileDto;
import com.tompai.ftpan.web.entity.Resource;
import com.tompai.ftpan.web.entity.ResourceChunk;
import com.tompai.ftpan.web.exception.FeitianException;
import com.tompai.ftpan.web.service.FileService;
import com.tompai.ftpan.web.service.ResourceService;
import com.tompai.ftpan.web.util.FeitianFileUtils;
import com.tompai.ftpan.web.util.FeitianUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @author tompai
 * @date 2020-01-27
 */
@Slf4j
@Service
public class ResourceServiceImpl implements ResourceService {

	private final FeitianProperties feitianProperties;
	private final ResourceChunkMapper chunkMapper;
	private final ResourceMapper resourceMapper;
	private final FileService fileService;

	@Autowired
	public ResourceServiceImpl(ResourceChunkMapper chunkMapper, FeitianProperties feitianProperties,
			ResourceMapper resourceMapper, FileService fileService) {
		this.chunkMapper = chunkMapper;
		this.feitianProperties = feitianProperties;
		this.resourceMapper = resourceMapper;
		this.fileService = fileService;
	}

	@Override
	public boolean checkChunk(ResourceChunk chunk) {
		ResourceChunk resourceChunk = chunkMapper.findByIdentifierAndChunkNumber(chunk.getIdentifier(),
				chunk.getChunkNumber());
		return Objects.nonNull(resourceChunk);
	}

	@Override
	public void saveChunk(ResourceChunk chunk) {
		String chunkDirPath = getChunkTmpDir(chunk.getIdentifier());
		String chunkPath = getChunkTmpPath(chunk);
		try {
			if (!Files.isWritable(Paths.get(chunkDirPath))) {
				Files.createDirectories(Paths.get(chunkDirPath));
			}
			Path p = Paths.get(chunkPath);
			Files.write(p, chunk.getFile().getBytes());

			chunkMapper.save(chunk);

		} catch (Exception e) {
			log.error(e.getMessage() + "{}", e);
			throw new FeitianException(String.format("保存块[%s]失败", chunkPath));
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void mergeChunk(MergeFileDto mergeFileDto) {
		// 合并文件到目标文件夹，并删除临时文件
		String uuid = mergeChunks(mergeFileDto);
		// 创建Resource记录和File记录
		generateRecord(mergeFileDto, uuid);
	}

	private String getChunkTmpDir(String identifier) {
		return feitianProperties.getResourceTmpDir() + File.separator + identifier;
	}

	private String getChunkTmpPath(ResourceChunk chunk) {
		return getChunkTmpDir(chunk.getIdentifier()) + File.separator + chunk.getFilename() + "-"
				+ chunk.getChunkNumber();
	}

	private String mergeChunks(MergeFileDto mergeFileDto) {
		String targetFileDir = feitianProperties.getBasicResourcePath() + File.separator
				+ FeitianUtils.formatDate(mergeFileDto.getCreateTime(), "yyyy") + File.separator
				+ FeitianUtils.formatDate(mergeFileDto.getCreateTime(), "MM");
		FeitianUtils.createFolders(targetFileDir);
		String uuid = FeitianUtils.uuid();
		String targetFilePath = targetFileDir + File.separator + uuid
				+ FeitianFileUtils.extractFileExtensionName(mergeFileDto.getFileName());

		try {
			Files.createFile(Paths.get(targetFilePath));
			Files.list(Paths.get(getChunkTmpDir(mergeFileDto.getIdentifier())))
					.filter(path -> !path.getFileName().toString().equals(mergeFileDto.getFileName()))
					.sorted((o1, o2) -> {
						String p1 = o1.getFileName().toString();
						String p2 = o2.getFileName().toString();
						int i1 = p1.lastIndexOf("-");
						int i2 = p2.lastIndexOf("-");
						return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));
					}).forEach(path -> {
						try {
							Files.write(Paths.get(targetFilePath), Files.readAllBytes(path), StandardOpenOption.APPEND);
							Files.delete(path);
						} catch (IOException e) {
							log.error(e.getMessage() + "{}", e);
							throw new FeitianException(e.getMessage());
						}
					});
			FeitianUtils.deleteFile(getChunkTmpDir(mergeFileDto.getIdentifier()));
			chunkMapper.deleteChunk(mergeFileDto.getIdentifier());
		} catch (Exception e) {
			log.error(e.getMessage() + "{}", e);
			throw new FeitianException(e.getMessage());
		}
		return uuid;
	}

	private void generateRecord(MergeFileDto fileDto, String uuid) {
		Resource resource = Resource.builder().link(1)
				.path(FeitianUtils.formatDate(fileDto.getCreateTime(), "yyyy") + File.separator
						+ FeitianUtils.formatDate(fileDto.getCreateTime(), "MM") + File.separator + uuid
						+ FeitianFileUtils.extractFileExtensionName(fileDto.getFileName()))
				// TODO md5校验
				.md5(FeitianUtils.uuid()).size(fileDto.getSize()).build();
		resourceMapper.save(resource);
		fileDto.setResourceId(resource.getId());
		fileDto.setType(FeitianFileUtils.getFileType(fileDto.getFileName()).toString());
		fileService.createFile(fileDto);
	}
}
